import fs from 'fs';
import path from 'path';
import { ApplicationFileInfo, FileDiffInfo, DiffStatus } from '../../typedef/diff_compile_files/diff_app_compile_files';

export class DiffCompileFiles {
  /**
 * 比较文件差异
 * @param oldFilePath
 * @param newFilePath
 * @returns
 */
static diffFile(oldFilePath: string, newFilePath: string): boolean {
  const oldFileContent: string = fs.readFileSync(oldFilePath, 'utf-8');
  const newFileContent: string = fs.readFileSync(newFilePath, 'utf-8');
  if (oldFileContent !== newFileContent) {
    return false;
  }
  return true;
}

/**
 * 入口函数
 * @param applicationPath 
 * @returns 
 */
static readApplicationFiles(applicationPath: string, appName: string): FileDiffInfo[] { 
  const beforeOfChangeFilePath: string = path.resolve(applicationPath, './old/build');
  const afterOfChangeFilePath: string = path.resolve(applicationPath, './new/build');
  const applicationFileInfo: ApplicationFileInfo = {
    applicationPath: applicationPath,
    beforeOfChangeFilePaths: this.readFilesInDir(beforeOfChangeFilePath, this.filterBuildResult),
    afterOfChangeFilePaths: this.readFilesInDir(afterOfChangeFilePath, this.filterBuildResult),
  };
  
  return this.diffApplication(applicationFileInfo, appName);;
}

static filterBuildResult(name: string): boolean {
  // return true;
  return name.endsWith('.json') || name.endsWith('.js') || name.endsWith('.ts') || name.endsWith('.ets')
}

static readFilesInDir(dirName: string, filter?: (name: string) => boolean): Array<string> {
  if (!fs.existsSync(dirName)) {
    return [];
  }
  const files: Array<string> = [];
  fs.readdirSync(dirName, { withFileTypes: true }).forEach((dir) => {
    const filePath: string = path.join(dirName, dir.name);
    if (dir.isFile()) {
      if (!filter) {
        files.push(filePath);
        return;
      }
      if (filter(dir.name)) {
        files.push(filePath);
      }
      return;
    }
    files.push(...this.readFilesInDir(filePath, filter));
  });
  return files;
}

static diffApplication(applicationFileInfo: ApplicationFileInfo, appName: string): FileDiffInfo[] {
  const fileDiffInfos: FileDiffInfo[] = [];
  const changeFiles: string[] = [];
  const beforeOfChangeFilePathSet: Set<string> = this.getRelativePathSet(
    path.resolve(applicationFileInfo.applicationPath, './old/build'),
    applicationFileInfo.beforeOfChangeFilePaths
  );
  const afterChangeFilePathSet: Set<string> = this.getRelativePathSet(
    path.resolve(applicationFileInfo.applicationPath, './new/build'),
    applicationFileInfo.afterOfChangeFilePaths
  );
  applicationFileInfo.afterOfChangeFilePaths.forEach((afterFilePath: string)=>{
    const afterRelativePath: string = path.relative(path.resolve(applicationFileInfo.applicationPath, './new/build'), afterFilePath);
    if (!beforeOfChangeFilePathSet.has(afterRelativePath)) {
      fileDiffInfos.push({
        fileName: afterRelativePath,
        status:DiffStatus.ADD,
        appName: appName,
      });
    } else if (changeFiles.indexOf(afterRelativePath) === -1) {
      changeFiles.push(afterRelativePath);
    }
  });
  applicationFileInfo.beforeOfChangeFilePaths.forEach((beforeFilePath: string)=> {
    const beforeRelativePath: string = path.relative(path.resolve(applicationFileInfo.applicationPath, './old/build'), beforeFilePath);
    if(!afterChangeFilePathSet.has(beforeRelativePath)) {
      fileDiffInfos.push({
        fileName:beforeRelativePath,
        status:DiffStatus.DELETE,
        appName: appName,
      });
    }else if (changeFiles.indexOf(beforeRelativePath) === -1) {
      changeFiles.push(beforeRelativePath);
    }
  });
  changeFiles.forEach((changeFile: string)=>{
    const beforePath: string = path.resolve(applicationFileInfo.applicationPath, './old/build', changeFile);
    const afterPath: string = path.resolve(applicationFileInfo.applicationPath, './new/build', changeFile);
    if (!this.diffFile(beforePath, afterPath)) {
      fileDiffInfos.push({
        fileName: changeFile,
        status:DiffStatus.CHANGE,
        appName: appName,
      });
    } else {
      fileDiffInfos.push({
        fileName: changeFile,
        status:DiffStatus.NORMAL,
        appName: appName,
      });
    }
  });

  return fileDiffInfos;
}

static getRelativePathSet(relativePath: string, filePaths: string[]): Set<string> {
  const relativePaths: Set<string> = new Set([]);
  filePaths.forEach((filePath: string) => {
    relativePaths.add(path.relative(relativePath, filePath));
  });
  return relativePaths;
}
}


